package com.huawei.java.main;

import com.huawei.java.pojo.EdgeNode;
import com.huawei.java.tools.csvreaders.ConfigReader;
import com.huawei.java.tools.csvreaders.QosReader;
import com.huawei.java.tools.csvreaders.SiteBandwidthReader;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

public class Main {
	//static final String WINDOWS_PRE = "D:/文档类/huawei2022";
	static final String CONFIG_INI =  "/data/config.ini";
	static final String DEMAND_CSV =  "/data/demand.csv";
	static final String QOS_CSV =  "/data/qos.csv";
	static final String SITE_BANDWIDTH_CSV =  "/data/site_bandwidth.csv";
	static final String OUTDIR =  "/output/solution.txt";
	public static void main(String[] args){
		//todo
		System.out.println("version1");
		long mill = System.currentTimeMillis();
		//System.out.println("开始处理输出路径");
		//处理输出路径
		File file = new File(OUTDIR);
		BufferedWriter bw = null;
		if(!file.exists())
		{
			try {
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		try {
			bw = Files.newBufferedWriter(Paths.get(OUTDIR), StandardOpenOption.WRITE);
		} catch (IOException e) {
			e.printStackTrace();
		}
		//todo
		//System.out.println("输出路径处理结束");

		// 读取qos限制
		//todo
		//System.out.println("开始处理qos");
		ConfigReader configReader = new ConfigReader();
		final int qosConstraint = configReader.readFile(CONFIG_INI);
		//todo
		//System.out.println("处理qos完成");

		//废弃的优先级方法
		//将边缘节点装入优先队列中
		//todo
		//System.out.println("开始处理边缘节点");
		//PriorityQueue<EdgeNode> emptyPriorityQueue = new PriorityQueue<>(comparator);
		//SiteBandwidthReader siteBandwidthReader = new SiteBandwidthReader();
		//siteBandwidthReader.readFile(SITE_BANDWIDTH_CSV, emptyPriorityQueue);
		//int edgeNodeNumber = emptyPriorityQueue.size();

		ArrayList<EdgeNode> emptyEdgeNodeList = new ArrayList<>();
		SiteBandwidthReader siteBandwidthReader = new SiteBandwidthReader();
		siteBandwidthReader.readFile(SITE_BANDWIDTH_CSV, emptyEdgeNodeList);
		int edgeNodeNumber = emptyEdgeNodeList.size();

		//todo
		//System.out.println("处理边缘节点完成");

		//读取qosMap
		//todo
		//System.out.println("开始处理qosMap");
		HashMap<String, Integer> qosMap = new HashMap<>(135*35);
		QosReader qosReader = new QosReader();
		qosReader.readFile(QOS_CSV, qosMap);
		//todo
		//System.out.println("处理qosMap完成");

		//读取demand.csv 处理带宽序列
		Path demandPath = Paths.get(DEMAND_CSV);
		try {
			BufferedReader br = Files.newBufferedReader(demandPath);
			//读取demand.csv第一行
			String s = br.readLine();
			String[] clientNames = s.split(",");
			//todo
			//System.out.println("处理完第一行,开始处理正文");

			//从第一行正文开始读
			s = br.readLine();
			while(s!=null)
			{
				//todo
				//System.out.println("处理正文-->"+s);

				String[] demandLine = s.split(",");

				//废弃的优先级队列方法
				//每次循环需要清理edgeNodesPriorityQueue
				//todo:change
				//PriorityQueue<EdgeNode> edgeNodesPriorityQueue = deepCopy(emptyPriorityQueue);
				//PriorityQueue<EdgeNode> edgeNodesPriorityQueue = emptyPriorityQueue;
				//clearQueue(edgeNodesPriorityQueue);
				ArrayList<EdgeNode> edgeNodeArrayList = deepCopy(emptyEdgeNodeList);
				int start = 0 % edgeNodeNumber ;

				//从第一个客户机开始处理
				for(int i=1;i<demandLine.length;i++)
				{
					//todo
					//System.out.println("处理当前第"+i+"个客户节点");
					//当前客户机名称
					String clientName = clientNames[i];
					//当前客户机需求量
					int clientDemand = Integer.parseInt(demandLine[i]);
					//一次最小分配的量
					//todo:tochange
					//int minDistributeBandwidth = clientDemand / edgeNodeNumber * 2 + 1;
					int minDistributeBandwidth = clientDemand;
					//每个客户节点分配的方案
					HashMap<String, Integer> distributeMap = new HashMap<>(135);
					//废弃方法:接受不符合放入要求的edge节点
					//ArrayList<EdgeNode> receiveList = new ArrayList<>();

					//todo
					//System.out.println("开始尝试demand="+clientDemand);
					//遍历边缘节点,尝试折半分配法
					while(clientDemand>0)
					{
						EdgeNode edgeNode = null;
						//先找一个满足qos的节点
						int clientEdgeQos = qosConstraint + 1;
						while(clientEdgeQos>=qosConstraint)
						{
							//废弃的优先级队列取法从edgeNodesPriorityQueue中取出
							//edgeNode = edgeNodesPriorityQueue.poll();

							edgeNode = edgeNodeArrayList.get(start);
							start = (start+1)%edgeNodeNumber;

							//先判断qos是否符合要求
							assert edgeNode != null;
							String qosName = clientName +"_" + edgeNode.name;
							clientEdgeQos = Math.min(qosMap.get(qosName), clientEdgeQos);

							//废弃的优先级方法
							//if(clientEdgeQos>qosConstraint){receiveList.add(edgeNode);}
						}
						assert edgeNode != null;
						//判断edgeNode能否装入一半
						int prepare;
						if(clientDemand<minDistributeBandwidth)
						{
							prepare = clientDemand;
						}
						else
						{
							prepare = clientDemand / 2;
						}
						//如果需求大了就缩减
						while(prepare>edgeNode.freeSpace)
						{
							prepare = prepare / 2;
						}
						Integer haveDistributed = distributeMap.getOrDefault(edgeNode.name, 0);
						distributeMap.put(edgeNode.name, haveDistributed+prepare);
						edgeNode.freeSpace -= prepare;

						//废弃的优先级方法:当前节点塞回队列
						//edgeNodesPriorityQueue.add(edgeNode);

						clientDemand -= prepare;
						//todo
						//System.out.println("剩余demand="+clientDemand);
					}
					//废弃方法: 当前节点使用完毕,把不符合qos要求的节点,塞回队列
					//edgeNodesPriorityQueue.addAll(receiveList);

					//先写入客户节点名称
					assert bw != null;
					bw.write(clientName+":");
					//遍历distributeMap,输出第i个主机的分配方案
					int dotNum = distributeMap.size()-1;
					for(String edgeName: distributeMap.keySet())
					{
						Integer value = distributeMap.get(edgeName);
						bw.write("<"+edgeName+","+value+">");
						if(dotNum>0)
						{
							bw.write(",");
							--dotNum;
						}
					}
					//注意最后一行换行符的问题
					if(i!=demandLine.length-1)
					{
						bw.write("\n");
					}
				}
				//换行并处理文件末尾回车问题
				s = br.readLine();
				if(s!=null)
				{
					bw.write("\n");
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		if(bw!=null)
		{
			try {
				bw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		//todo
		System.out.println(System.currentTimeMillis() - mill);
	}

	/**
	 * 边缘节点的比较器
	 */
	static Comparator<EdgeNode> comparator = new Comparator<EdgeNode>() {
		@Override
		public int compare(EdgeNode o1, EdgeNode o2) {
			return (int) (o2.freeSpace - o1.freeSpace);
		}
	};

	/**
	 * 清理队列
	 * @param edgeNodesPriorityQueue
	 */
	static void clearQueue(PriorityQueue<EdgeNode> edgeNodesPriorityQueue)
	{
		ArrayList<EdgeNode> list = new ArrayList<>();
		while(edgeNodesPriorityQueue.size()>0)
		{
			EdgeNode node = edgeNodesPriorityQueue.poll();
			node.freeSpace = node.capability;
			list.add(node);
		}
		edgeNodesPriorityQueue.addAll(list);
	}

	/**
	 * queue使用拷贝的话
	 */
	static PriorityQueue<EdgeNode> deepCopy(PriorityQueue<EdgeNode> edgeNodesPriorityQueue)
	{
		PriorityQueue<EdgeNode> edgeNodes = new PriorityQueue<>();
		EdgeNode[] array = new EdgeNode[edgeNodesPriorityQueue.size()];
		edgeNodesPriorityQueue.toArray(array);
		edgeNodes.addAll(Arrays.asList(array));
		return edgeNodes;
	}

	/**
	 * 深复制arraylist
	 */
	static ArrayList<EdgeNode> deepCopy(ArrayList<EdgeNode> list)
	{
		ArrayList<EdgeNode> out = new ArrayList<>();
		for(EdgeNode node: list)
		{
			out.add((EdgeNode) node.clone());
		}
		return out;
	}
}
